﻿using System;
using System.Collections.Generic;

namespace zijian666.AnyExtensions.Privates;

/// <summary>
/// 内部私有扩展方法
/// </summary>
internal static class InternalExtensions
{
    private static readonly HashSet<Type> INTEGER_TYPES =
    [
        typeof(sbyte),
        typeof(byte),
        typeof(short),
        typeof(ushort),
        typeof(int),
        typeof(uint),
        typeof(long),
        typeof(ulong)
    ];

    /// <summary>
    /// 类型转换，如果转换失败抛出异常
    /// </summary>
    /// <exception cref="InvalidCastException">当转换失败时抛出异常</exception>
    public static T? ConvertTo<T>(this object value)
        => value switch
        {
            IConvertible and not string => (T?)value.ChangeType(typeof(T), true),
            _ => (value + "").ConvertTo<T>()
        };

    /// <summary>
    /// 类型转换，如果转换失败抛出异常
    /// </summary>
    /// <exception cref="InvalidCastException">当转换失败时抛出异常</exception>
    public static T? ConvertTo<T>(this string value)
    {
        if (GenericHelper<T>.TryParse(value, out var result))
        {
            return result;
        }
        if (GenericHelper<T>.IsInteger && GenericHelper<decimal>.TryParse(value, out var dec))
        {
            return (T?)ChangeType(dec, typeof(T), true);
        }
        throw new InvalidCastException($"无法将值{value}转为{typeof(T).Name}类型");
    }

    /// <summary>
    /// 判断类型是否是整型
    /// </summary>
    public static bool IsInteger(this Type? type)
    {
        if (type is null)
        {
            return false;
        }
        if (INTEGER_TYPES.Contains(type))
        {
            return true;
        }
        if (type.IsEnum)
        {
            return IsInteger(Enum.GetUnderlyingType(type));
        }
        return IsInteger(Nullable.GetUnderlyingType(type));
    }

    /// <summary>
    /// 对象类型转换
    /// </summary>
    /// <exception cref="InvalidCastException">当转换失败 且 <paramref name="throwIfFail"/> 为true 时抛出异常</exception>
    public static object? ChangeType(this object? value, Type conversionType, bool throwIfFail = true)
    {
        try
        {
            if (conversionType.IsInstanceOfType(value))
            {
                return value;
            }
            if (Nullable.GetUnderlyingType(conversionType) is Type structType)
            {
                if (value is null or DBNull)
                {
                    return null;
                }
                conversionType = structType;
            }
            else if ((value is null || value is DBNull) && !conversionType.IsValueType)
            {
                return null;
            }
            if (conversionType.IsEnum)
            {
                if (value is string str)
                {
                    return Enum.Parse(conversionType, str, true);
                }
                var enumType = Enum.GetUnderlyingType(conversionType);
                if (!enumType.IsInstanceOfType(value))
                {
                    value = ChangeType(value, enumType, true)!;
                }
                return Enum.ToObject(conversionType, value);
            }
            if (conversionType == typeof(Uri))
            {
                var url = value + "";
                if (url is not null)
                {
                    if (!url.StartsWith("https://", StringComparison.OrdinalIgnoreCase)
                        && !url.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
                    {
                        if (url.StartsWith("://", StringComparison.OrdinalIgnoreCase))
                        {
                            url = "http" + url;
                        }
                        else
                        {
                            url = "http://" + url;
                        }
                    }
                }
                if (Uri.TryCreate(url, UriKind.Absolute, out var uri))
                {
                    return uri;
                }
            }
            if (conversionType == typeof(decimal))
            {
                if (value is float f)
                {
                    return f.ToDecimal();
                }
                if (value is double d)
                {
                    return d.ToDecimal();
                }
            }

            return Convert.ChangeType(value, conversionType);
        }
        catch
        {

        }
        if (IsInteger(conversionType))
        {
            try
            {
                var dec = Convert.ChangeType(value, typeof(decimal));
                return Convert.ChangeType(dec, conversionType);
            }
            catch
            {

            }
        }
        if (throwIfFail)
        {
            if (value is null)
            {
                throw new InvalidCastException($"无法将 null 转为{conversionType.Name}类型");
            }
            throw new InvalidCastException($"无法将值{value}({value.GetType().Name})转为{conversionType.Name}类型");
        }
        return null;
    }
}